package com.lineage.server.model.Instance.npcai;

import com.lineage.server.model.Instance.L1NpcInstance;
import com.lineage.server.model.map.L1Map;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class CheckPath {
  private static final Log _log = LogFactory.getLog(CheckPath.class);
  
  private Square _goal;
  
  private Square _start;
  
  private final Square[][] _elements;
  
  private final int _cx;
  
  private final int _cy;
  
  private final int _rows;
  
  private final int _columns;
  
  private final ArrayList<Square> _opened = new ArrayList<>();
  
  private final HashSet<Square> _closed = new HashSet<>();
  
  private final ArrayList<int[]> _bestList = (ArrayList)new ArrayList<>();
  
  public CheckPath(int tx, int ty, int hc, L1NpcInstance npc) {
    int x = npc.getX();
    int y = npc.getY();
    int x1 = x - hc;
    int y1 = y - hc;
    int x2 = x + hc;
    int y2 = y + hc;
    this._rows = x2 - x1;
    this._columns = y2 - y1;
    this._cx = x1;
    this._cy = y1;
    int mx = x2 - this._rows;
    int my = y2 - this._columns;
    this._elements = new Square[this._rows][this._columns];
    createSquares(npc);
    setStartAndGoal(x - mx, y - my, tx - mx, ty - my);
    init();
  }
  
  public int[] cxy() {
    return new int[] { this._cx, this._cy };
  }
  
  protected int getRows() {
    return this._rows;
  }
  
  protected int getColumns() {
    return this._columns;
  }
  
  protected Square getSquare(int x, int y) {
    return this._elements[x][y];
  }
  
  private void init() {
    generateAdjacenies();
  }
  
  public void draw() {
    for (int i = 0; i < this._rows; i++) {
      for (int j = 0; j < this._columns; j++) {
        Square square = this._elements[i][j];
        drawLeft(square);
      } 
    } 
  }
  
  private void drawLeft(Square square) {
    String out = null;
    for (int[] i : this._bestList) {
      if (square.getX() == i[0] && square.getY() == i[1]) {
        if (square.isEnd()) {
          out = "PC";
          continue;
        } 
        out = "^^";
      } 
    } 
    if (out == null)
      if (square.isStart()) {
        out = "NP";
      } else if (square.isEnd()) {
        out = "PC";
      } else if (square.is_open()) {
        out = "  ";
      } else {
        out = "##";
      }  
    System.out.print(out);
  }
  
  private void setStartAndGoal(int x, int y, int tx, int ty) {
    try {
      this._start = this._elements[x][y];
      this._start.setStart(true);
      this._goal = this._elements[tx][ty];
      this._goal.setEnd(true);
    } catch (Exception exception) {}
  }
  
  private void generateAdjacenies() {
    try {
      for (int i = 0; i < this._rows; i++) {
        for (int j = 0; j < this._columns; j++)
          this._elements[i][j].calculateAdjacencies(); 
      } 
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } 
  }
  
  private void createSquares(L1NpcInstance npc) {
    try {
      L1Map map = npc.getMap();
      for (int i = 0; i < this._rows; i++) {
        for (int j = 0; j < this._columns; j++) {
          Square square = this._elements[i][j] = new Square(i, j, this);
          int cx = this._cx + i;
          int cy = this._cy + j;
          if (map.isPassableDna(cx, cy, 0))
            square.set_open(true); 
        } 
      } 
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } 
  }
  
  public ArrayList<int[]> findBestPath() {
    try {
      HashSet<Square> adjacencies = this._start.getAdjacencies();
      Iterator<Square> iter;
      for (iter = adjacencies.iterator(); iter.hasNext(); ) {
        Square adjacency = iter.next();
        adjacency.setParent(this._start);
        if (!adjacency.isStart())
          this._opened.add(adjacency); 
      } 
      for (; this._opened.size() > 0; iter.hasNext()) {
        Square best = findBestPassThrough();
        this._opened.remove(best);
        this._closed.add(best);
        if (best.isEnd()) {
          populateBestList(this._goal);
          this._opened.clear();
          this._closed.clear();
          return this._bestList;
        } 
        HashSet<Square> neighbors = best.getAdjacencies();
        iter = neighbors.iterator();
        Square neighbor = iter.next();
        if (this._opened.contains(neighbor)) {
          Square tmpSquare = new Square(neighbor.getX(), neighbor.getY(), this);
          tmpSquare.setParent(best);
          if (!tmpSquare.is_open()) {
            this._opened.remove(tmpSquare);
            continue;
          } 
        } 
        if (this._closed.contains(neighbor)) {
          Square tmpSquare = new Square(neighbor.getX(), neighbor.getY(), this);
          tmpSquare.setParent(best);
          if (!tmpSquare.is_open()) {
            this._closed.remove(tmpSquare);
            continue;
          } 
        } 
        neighbor.setParent(best);
        this._opened.remove(neighbor);
        this._closed.remove(neighbor);
        this._opened.add(0, neighbor);
        continue;
      } 
      this._opened.clear();
      this._closed.clear();
      this._bestList.clear();
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } 
    return this._bestList;
  }
  
  private void populateBestList(Square square) throws Exception {
    try {
      if (square == null)
        return; 
      this._bestList.add(0, new int[] { square.getX(), square.getY() });
      if (!square.getParent().isStart())
        populateBestList(square.getParent()); 
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
    } 
  }
  
  private Square findBestPassThrough() {
    try {
      Square best = null;
      for (Iterator<Square> iter = this._opened.iterator(); iter.hasNext(); ) {
        Square square = iter.next();
        if (best == null || square.is_open())
          best = square; 
      } 
      return best;
    } catch (Exception e) {
      _log.error(e.getLocalizedMessage(), e);
      return null;
    } 
  }
}
